<template>
  <div>
    <div class="shopcart">
      <div class="content" @click="toggleList">
        <div class="content-left">
          <div class="logo-wrapper">
            <div class="logo" :class="{ 'highlight': totalCount > 0 }">
              <i class="icon-shopping_cart" :class="{ 'highlight': totalCount > 0 }"></i>
            </div>
            <div class="num" v-show="totalCount > 0">
              <bubble :num="totalCount"></bubble>
            </div>
          </div>
          <div class="price" :class="{ 'highlight': totalPrice > 0 }">￥{{totalPrice}}</div>
          <div class="desc">另需配送费￥{{deliveryPrice}}元</div>
        </div>
        <div class="content-right">
        <!-- 不想让他冒泡到上一层的点击，因此使用click.stop方法 -->
        <!-- 或者让pay方法不冒泡也可以 -->
          <div @click="pay" class="pay" :class="payClass">{{payDesc}}</div>
        </div>
      </div>
      <!-- 小球是fix布局绝对定位 -->
      <div class="ball-container">
        <div v-for="(ball,index) in balls" :key="index">
          <transition
          @before-enter="beforeDrop"
          @enter="dropping"
          @after-enter="afterDrop">
            <!-- 小球的抛物线动画是分为横向和纵向动，因此需要inner和inner-hook两个class -->
            <div class="ball" v-show="ball.show">
              <div class="inner inner-hook"></div>
            </div>
          </transition>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Bubble from 'components/bubble/bubble'
// 默认的小球数量为 10
const BALL_LEN = 10
const innerClsHook = 'inner-hook'

// 定义一个辅助函数
function createBalls() {
  const ret = []
  for (let i = 0; i < BALL_LEN; i++) {
    // 给每个小球定义一个属性为false
    ret.push({
      show: false
    })
  }
  return ret
}

export default {
  name: 'shop-cart',
  props: {
    // 已选择的商品
    selectFoods: {
      type: Array,
      default() {
          return []
      }
    },
    // 配送费
    deliveryPrice: {
      type: Number,
      default: 0
    },
    // 最小起送费
    minPrice: {
      type: Number,
      default: 0
    },
    // 将下面的 listFold 变成一个prop，起名fold；（创建后会删除，起替换作用）
    // fold 用来处理 shopcartsticky 的多次出现使用；shopcartsticky默认是展开的
    fold: {
      type: Boolean,
      default: true
    },
    sticky: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // 定义一个balls变量,因为需要一些默认的小球
      // balls: []
      balls: createBalls(),
      // 放在 data 里 是因为 Vue 规定不能直接修改fold的值
      // 这里可以做到用data()方法去接收，然后再修改data里的fold的值
      listFold: this.fold
    }
  },
  // 计算属性是用来展示用的
  computed: {
    // 价钱总和
    totalPrice() {
      let total = 0
    // 遍历每个商品，然后加和每个商品的价格
      this.selectFoods.forEach(food => {
        total += food.price * food.count
      })
      return total
    },
    // 购买总数量
    totalCount() {
      let count = 0
      this.selectFoods.forEach(food => {
        count += food.count
      })
      return count
    },
    // 支付描述
    payDesc() {
      if (this.totalPrice === 0) {
        return `￥${this.minPrice}元起送`
      } else if (this.totalPrice < this.minPrice) {
        const diff = this.minPrice - this.totalPrice
        return `还差￥${diff}元起送`
      } else {
        return '去结算'
      }
    },
    // 根据不同的逻辑显示不同的class，因为设置大于配送费才能显示去结算。
    payClass() {
      if (!this.totalPrice || this.totalPrice < this.minPrice) {
        return 'not-enough'
      } else {
        return 'enough'
      }
    }
  },
  // 添加一个watch方法来观测fold的值
  watch: {
    fold(newVal) {
      this.listFold = newVal
    },
    // 解决无商品了以后购物车列表还打开的bug
    totalCount(newVal) {
      // 当商品列表展开时并且没有商品时
      if (!this.listFold && !newVal) {
        this._hideShopCartList()
      }
    }
  },
  // 加一个生命周期函数，里面放 下落的小球 这样一个数组中
  created() {
    // 不放在data中，因为掉落的小球这个数组不需要响应式支持
    this.dropBalls = []
    // 在shop-cart-list中使用
    // 定义一个list是否打开的标志位listFold
    // 用来表示购物车为是否收起，默认为true
    // ——————————————————————————
    // 当添加 shopcartSticky 后就不需要这个值了
    // ——————————————————————————
    // this.listFold = true
  },
  methods: {
    // el(element)就是接收按钮的位置
    drop(el) {
      for (let i = 0; i < this.balls.length; i++) {
        const ball = this.balls[i]
        if (!ball.show) {
          ball.show = true
          // 把el的状态保存到ball的状态中
          ball.el = el
          this.dropBalls.push(ball)
          return
        }
      }
    },
    beforeDrop(el) {
      // 拿到最后一个也就是最新的一个dropBalls
      const ball = this.dropBalls[this.dropBalls.length - 1]
      // 然后根据这个小球拿到它的位置信息
      const rect = ball.el.getBoundingClientRect()
      // 32 是购物车正中心离屏幕左侧的距离
      const x = rect.left - 32
      // window.innerHeight 是浏览器可视窗口的大小，22是购物车正中心离屏幕底侧的距离
      // y方向是负值是因为，一开始小球要向y的负方向移动
      const y = -(window.innerHeight - rect.top - 22)
      el.display = ''
      // webkit就是支持 Safari 和 Chrome(兼容低端浏览器版本)
      // 最后设置x方向和y方向的移动的初始位置
      el.style.transform = el.style.webkitTransform = `translate3d(0, ${y}px, 0)`
      const inner = el.getElementsByClassName(innerClsHook)[0]
      inner.style.transform = inner.style.webkitTransform = `translate3d(${x}px, 0, 0)`
    },
    // done是告诉transition已经执行完了
    dropping(el, done) {
      // 首先要触发一次重绘（？）
      this._reflow = document.body.offsetHeight
      // 设置x方向和y方向的移动的目标位置
      el.style.transform = el.style.webkitTransform = 'translate3d(0, 0, 0)'
      const inner = el.getElementsByClassName(innerClsHook)[0]
      inner.style.transform = inner.style.webkitTransform = 'translate3d(0, 0, 0)'
      // 动画结束之后要监听 transitionend ，告诉vue transition结束了
      el.addEventListener('transitionend', done)
    },
    afterDrop(el) {
      // 去除做动画的ball
      const ball = this.dropBalls.shift()
      if (ball) {
        ball.show = false
        el.style.display = 'none'
      }
    },
    // 用来切换shop-cart-list打开关闭的方法
    toggleList() {
      // 根据标志位决定是否展开
      if (this.listFold) {
        // 如果可以展开，则根据业务是当有商品的时候才展开，没有的话不展开
        if (!this.totalCount) {
          return
        }
        // 然后修改标志位
        this.listFold = false
        this._showShopCartList()
        this._showShopCartSticky()
      } else {
        this.listFold = true
        this._hideShopCartList()
      }
    },
    // 结算组件，使用CubeUI中的Dialog组件
    pay(e) {
      if (this.totalPrice < this.minPrice) {
        return
      }
      // 这时候不能用缓存,不然当他判断有值了以后就不会再次create，这样的话值就会一直保持第一次的值
      // 这里也可以使用$props做响应式
      this.dialogComp = this.$createDialog({
        title: '支付',
        content: `您需要支付共${this.totalPrice}元`
      })
      this.dialogComp.show()
      e.stopPropagation()
    },
    _showShopCartList() {
      // 2. 并且将值放在 shopCartListComp 中
      // 3. 做一个缓存：使用 "或" 来连接
      this.shopCartListComp =
        this.shopCartListComp ||
        // 1. 这个 createShopCartList 方法将 selectFoods(上面的 props 里的selectFoods) 作为props传进去
        this.$createShopCartList({
          $props: {
            selectFoods: 'selectFoods'
          },
          $events: {
            hide: () => {
              // 修改那个点击蒙层的问题，当调用了hide方法后，恒置为true
              this.listFold = true
            },
            leave: () => {
              this._hideShopCartSticky()
            },
            // 修改购物车里的小球飞入动画里添加的，监测 shop-cart-list 里的@add
            add: el => {
              this.shopCartStickyComp.drop(el)
            }
          }
        })
      this.shopCartListComp.show()
    },
    // 调用shopcart 副本：ShopCartSticky 时的方法
    _showShopCartSticky() {
      this.shopCartStickyComp =
        this.shopCartStickyComp ||
        this.$createShopCartSticky({
          // 一定要传成响应式的
          $props: {
            selectFoods: 'selectFoods',
            deliveryPrice: 'deliveryPrice',
            minPrice: 'minPrice',
            fold: 'listFold',
            list: this.shopCartListComp
          }
        })
      this.shopCartStickyComp.show()
    },
    _hideShopCartList() {
      const comp = this.sticky ? this.$parent.list : this.shopCartListComp
      // 查看food时，没有创建shopCartList，监听totalCount，执行_hideShopCartList时，会找不到this.$parent.list，直接判断comp.hide，会报错，故加上comp的判断
      comp && comp.hide && comp.hide()
      // -----------------------------
      // 原始写法：因无法区分是哪一种情况hide，所以写成↑上面这种写法
      // this.$parent.list.hide()
      // this.shopCartListComp.hide()
    },
    _hideShopCartSticky() {
      this.shopCartStickyComp.hide()
    }
   },
  components: {
    Bubble
  }
}
</script>
<style lang="stylus" scoped>
@import '~common/stylus/mixin'
@import '~common/stylus/variable'
.shopcart
  height 100%
  .content
    display flex
    background $color-background
    font-size 0
    color $color-light-grey
    .content-left
      flex 1
      .logo-wrapper
        display inline-block
        vertical-align top
        position relative
        top -10px
        margin 0 12px
        padding 6px
        width 56px
        height 56px
        box-sizing border-box
        border-radius 50%
        background $color-background
        .logo
          width 100%
          height 100%
          border-radius 50%
          text-align center
          background $color-dark-grey
          &.highlight
            background $color-blue
          .icon-shopping_cart
            line-height 44px
            font-size $fontsize-large-xxx
            color $color-light-grey
            &.highlight
              color $color-white
        .num
          position absolute
          top 0
          right 0
      .price
        display inline-block
        vertical-align top
        margin-top 12px
        line-height 24px
        padding-right 12px
        box-sizing border-box
        border-right 1px solid rgba(255, 255, 255, 0.1)
        font-weight 700
        font-size $fontsize-large
        &.highlight
          color $color-white
      .desc
        display inline-block
        vertical-align top
        margin 12px 0 0 12px
        line-height 24px
        font-size $fontsize-small-s
    .content-right
      flex 0 0 105px
      width 105px
      .pay
        height 48px
        line-height 48px
        text-align center
        font-weight 700
        font-size $fontsize-small
        &.not-enough
          background $color-dark-grey
        &.enough
          background $color-green
          color $color-white
  .ball-container
    .ball
      position fixed
      left 32px
      bottom 22px
      z-index 200
      transition all 0.8s cubic-bezier(0.49, -0.29, 0.75, 0.41)
      .inner
        width 16px
        height 16px
        border-radius 50%
        background $color-blue
        transition all 0.8s linear
</style>
